package com.yihaodian.index.meta.routing;

import java.util.Collection;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.yihaodian.cluster.Node;
import com.yihaodian.index.meta.shard.ShardId;
import com.yihaodian.index.meta.shard.ShardRouting;
import com.yihaodian.index.meta.shard.ShardRouting.Status;

public class RoutingTable {

	private String _indexId;
	private String _clusterId;

	// shard id -> node id
	private Multimap<String, String> shardMap = ArrayListMultimap.create();
	// node id -> shard id
	private Multimap<String, ShardRouting> nodeMap = ArrayListMultimap.create();

	public RoutingTable(String indexId) {
		this._indexId = indexId;
		// this._clusterId = clusterId;
	}

	// For json (de)serialization
	private RoutingTable() {
	}

	/**
	 * Get unique id "IndexId_ClusterId"
	 * 
	 * @return
	 */
	public String getId() {
		return getId(_indexId, _clusterId);
	}

	/**
	 * Get Index id
	 * 
	 * @return
	 */
	public String getIndexId() {
		return _indexId;
	}

	/**
	 * Get Cluster id
	 * 
	 * @return
	 */
	public String getClusterId() {
		return _clusterId;
	}

	/**
	 * Get deployed nodes
	 * 
	 * @param shard
	 * @return
	 */
	public synchronized Collection<String> getNodes(ShardId shardId) {
		return shardMap.get(shardId.getShardId());
	}

	/**
	 * Get deployed shards
	 * 
	 * @param shard
	 * @return
	 */
	public synchronized Collection<ShardRouting> getShardRouting(String nodeId) {
		return nodeMap.get(nodeId);
	}

	// public Collection<Shard> shards()
	// {
	// return shardMap.keys();
	// }
	//
	/**
	 * Add new routing entry
	 * 
	 * @param node
	 * @param shard
	 */
	public synchronized void addEntry(String nodeId, ShardRouting shardRouting) {
		nodeMap.put(nodeId, shardRouting);
		shardMap.put(shardRouting.getShardId().getShardId(), nodeId);
	}

	/**
	 * Remove routing entry
	 * 
	 * @param node
	 * @param shard
	 */
	public synchronized void removeEntry(String nodeId, ShardId shardId) {
		for (ShardRouting sr : nodeMap.get(nodeId)) {
			if (sr.getShardId().equals(shardId))
				;
			nodeMap.remove(nodeId, sr);
		}
		shardMap.remove(shardId.getShardId(), nodeId);
	}

	public void resetStatus() {
		for (ShardRouting sr : nodeMap.values()) {
			sr.setStatus(Status.INIT);
		}
	}

	public static String getId(String indexId, String clusterId) {
		return indexId + "_" + clusterId;
	}

	public static String getIndexId(String id) {
		String indexId = null;
		if (id.contains("_")) {
			String[] splits = id.split("_");
			if (splits != null && splits.length == 2) {
				indexId = splits[0];
			}
		}
		return indexId;
	}

	public static String getClusterId(String id) {
		String clusterId = null;
		if (id.contains("_")) {
			String[] splits = id.split("_");
			if (splits != null && splits.length == 2) {
				clusterId = splits[1];
			}
		}
		return clusterId;
	}

	public RoutingTable clone() {
		RoutingTable rt = new RoutingTable();
		rt._clusterId = this._clusterId;
		rt._indexId = this._indexId;
		rt.nodeMap = ArrayListMultimap.create(this.nodeMap);
		rt.shardMap = ArrayListMultimap.create(this.shardMap);

		return rt;
	}

	@Override
	public int hashCode() {
		return getId().hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof RoutingTable)) {
			return false;
		}

		RoutingTable other = (RoutingTable) obj;
		if (other._indexId == null ? this._indexId != null : !other._indexId
				.equals(this._indexId)) {
			return false;
		}

		if (other._clusterId == null ? this._clusterId != null
				: !other._clusterId.equals(this._clusterId)) {
			return false;
		}

		if (other.shardMap == null ? this.shardMap != null : !other.shardMap
				.equals(this.shardMap)) {
			return false;
		}

		if (other.nodeMap == null ? this.nodeMap != null : !other.nodeMap
				.equals(this.nodeMap)) {
			return false;
		}

		return true;
	}

	@Override
	public String toString() {
		return "RoutingTable [indexId=" + _indexId + ", _clusterId="
				+ _clusterId + ", shardMap=" + shardMap + ", nodeMap="
				+ nodeMap + "]";
	}

}
